#include "arp.h"
#include "base.h"
#include "types.h"
#include "ether.h"
#include "arpcache.h"
#include "log.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include <ip.h>

// handle arp packet
// If the dest ip address of this arp packet is not equal to the ip address of the incoming iface, drop it.
// If it is an arp request packet, send arp reply to the destination, insert the ip->mac mapping into arpcache.
// If it is an arp reply packet, insert the ip->mac mapping into arpcache.
// Tips:
// You can use functions: htons, htonl, ntohs, ntohl to convert host byte order and network byte order (16 bits use ntohs/htons, 32 bits use ntohl/htonl).
// You can use function: packet_to_ether_arp() in arp.h to get the ethernet header in a packet.
void handle_arp_packet(iface_info_t *iface, char *packet, int len)
{
	// assert(0 && "TODO: function handle_arp_packet not implemented!");
	if(!packet){
		log(ERROR,"packet not exist! ");
	}
	log(DEBUG,"start handling the arp packet");

	struct ether_arp* arp_hdr=packet_to_ether_arp(packet);
	// iface is host-byte order, packet is net-byte order
	if(ntohl((arp_hdr)->arp_tpa) != iface->ip){
		// Drop the packet
		log(INFO,"arp target address ip not equal ifaceip, drop the packet");
		return;
	}

	// Convert ARP opcode to host byte order
	u16 arp_op=ntohs(arp_hdr->arp_op);
	
	if(arp_op == ARPOP_REQUEST){
		// Handle ARP request
		// 发送回复，dst ip是请求包的源地址, arp_sha和arp_spa
		// 传入的参数都是host byte-order
		log(DEBUG,"coming ARP Request, sending Reply packet");
		arp_send_reply(iface,arp_hdr);
		log(DEBUG,"inserting the ip->mac");
		arpcache_insert(ntohl(arp_hdr->arp_spa),arp_hdr->arp_sha);
	}
	else if (arp_op==ARPOP_REPLY){
		// Handle ARP reply
		 // Insert IP-MAC mapping into ARP cache
		log(DEBUG,"coming ARP Reply, insert the IP-MAC mapping");
        arpcache_insert(ntohl(arp_hdr->arp_spa), arp_hdr->arp_sha);
	}
	
}

// send an arp reply packet
// Encapsulate an arp reply packet, send it out through iface_send_packet.
void arp_send_reply(iface_info_t *iface, struct ether_arp *req_hdr)
{
    log(DEBUG, "Preparing to send ARP reply");

    // Allocate memory for the packet (Ethernet + ARP)
    int len = ETHER_HDR_SIZE + sizeof(struct ether_arp);
    char *packet = (char *)malloc(len);
    if (!packet) {
        log(ERROR, "Failed to allocate memory for ARP reply packet");
        return;
    }

    // Fill in the Ethernet header
    struct ether_header *eh = (struct ether_header *)packet;
    memcpy(eh->ether_shost, iface->mac, ETH_ALEN);    // Source MAC
    memcpy(eh->ether_dhost, req_hdr->arp_sha, ETH_ALEN); // Destination MAC
    eh->ether_type = htons(ETH_P_ARP);               // Set EtherType to ARP

	//log(DEBUG, "Before fillin arp hdr, ether header dst MAC:" ETHER_STRING, 
   	//ETHER_FMT(eh->ether_dhost));


    // Fill in the ARP header
    struct ether_arp *arp_hdr = packet_to_ether_arp(packet);
    arp_hdr->arp_hrd = htons(ARPHRD_ETHER);          // Ethernet hardware
    arp_hdr->arp_pro = htons(ETH_P_IP);              // Protocol type (IP)
    arp_hdr->arp_hln = ETH_ALEN;                     // Hardware address length
    arp_hdr->arp_pln = 4;                            // Protocol address length
    arp_hdr->arp_op = htons(ARPOP_REPLY);            // Operation (Reply)
    memcpy(arp_hdr->arp_sha, iface->mac, ETH_ALEN);  // Sender MAC
    arp_hdr->arp_spa = htonl(iface->ip);             // Sender IP (network order)
    memcpy(arp_hdr->arp_tha, req_hdr->arp_sha, ETH_ALEN); // Target MAC
    arp_hdr->arp_tpa = req_hdr->arp_spa;             // Target IP (already in network order)

    // Send the packet

	log(DEBUG,"Sent ARP reply from " IP_FMT " to " IP_FMT,NET_IP_FMT_STR(arp_hdr->arp_spa),
		NET_IP_FMT_STR(arp_hdr->arp_tpa)
	);
	
	//log(DEBUG,"source MAC is " ETHER_STRING,ETHER_FMT(arp_hdr->arp_sha));
	// 一定要在发送之前打印，不然发送会free你的包,打印出来不对
	log(DEBUG, "Sent ARP Reply " ETHER_STRING " -> " ETHER_STRING, 
   	ETHER_FMT(eh->ether_shost),ETHER_FMT(eh->ether_dhost));
	
    iface_send_packet(iface, packet, len);

    log(DEBUG, "ARP reply sent successfully");
}



// send an arp request
// Encapsulate an arp request packet, send it out through iface_send_packet.
void arp_send_request(iface_info_t *iface, u32 dst_ip)
{
	// assert(0 && "TODO: function arp_send_request not implemented!");
	int len=ETHER_HDR_SIZE+sizeof(struct ether_arp);
	char *packet=(char*)malloc(len);
	if(!packet){
		log(ERROR,"malloc failed");
	}

	struct ether_arp* arp_hdr=packet_to_ether_arp(packet);

	// Fill in the ethernet header
	memcpy(arp_hdr->arp_sha, iface->mac, ETH_ALEN);
	arp_hdr->arp_spa=htonl(iface->ip);
	// We assume dst_ip is net-ordered
	arp_hdr->arp_tpa=htonl(dst_ip);
	arp_hdr->arp_hrd=htons(ARPHRD_ETHER);
	arp_hdr->arp_pro=htons(ETH_P_IP);
	arp_hdr->arp_hln=ETH_ALEN;
	arp_hdr->arp_pln=4;
	arp_hdr->arp_op=htons(ARPOP_REQUEST);

	// Set the target hardware address to all 0xFF
	// This is a broadcast address
	// for the ARP request
	u8 broad_cast_mac[ETH_ALEN]={0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
	memcpy(arp_hdr->arp_tha, broad_cast_mac, ETH_ALEN);

	// 设置以太头
	struct ether_header *eh = (struct ether_header *)packet;
	memcpy(eh->ether_shost,iface->mac,ETH_ALEN);
	memcpy(eh->ether_dhost,broad_cast_mac,ETH_ALEN);
	eh->ether_type=(htons)(ETH_P_ARP);

	// Send the packet
	
	log(DEBUG,"Sending ARP request from " IP_FMT " to " IP_FMT,NET_IP_FMT_STR(arp_hdr->arp_spa),
		NET_IP_FMT_STR(arp_hdr->arp_tpa)
	);
	log(DEBUG, "Sent ARP Request(BROADCAST) from " ETHER_STRING " to " ETHER_STRING, ETHER_FMT(eh->ether_shost),
   	ETHER_FMT(eh->ether_dhost));
	iface_send_packet(iface, packet, len);
	log(DEBUG,"packet sent");

	
}

// send (IP) packet through arpcache lookup 
// Lookup the mac address of dst_ip in arpcache.
// If it is found, fill the ethernet header and emit the packet by iface_send_packet.
// Otherwise, pending this packet into arpcache and send arp request.
void iface_send_packet_by_arp(iface_info_t *iface, u32 dst_ip, char *packet, int len)
{
	// assert(0 && "TODO: function iface_send_packet_by_arp not implemented!");
	struct ether_arp* arp_hdr=packet_to_ether_arp(packet);
	
	// 这里情形是我们不知道mac是什么，需要查找，通过arpcache_lookup函数隐式地返回给我们。
	u8 dst_mac[ETH_ALEN];

	// Lookup the MAC address in the ARP cache
	if(arpcache_lookup(dst_ip,dst_mac)){
		// If the MAC address is found, fill the ethernet header
        struct ether_header *eh = (struct ether_header *)packet;
        memcpy(eh->ether_shost, iface->mac, ETH_ALEN);
        memcpy(eh->ether_dhost, dst_mac, ETH_ALEN);
        eh->ether_type = htons(ETH_P_IP);

        log(DEBUG, "Sending packet from " IP_FMT " to " IP_FMT,
            HOST_IP_FMT_STR(iface->ip), HOST_IP_FMT_STR(dst_ip));
        log(DEBUG, "MAC is from " ETHER_STRING " to " ETHER_STRING,
            ETHER_FMT(eh->ether_shost), ETHER_FMT(eh->ether_dhost));

        // Send the packet
        iface_send_packet(iface, packet, len);
        return;
	}
	
	// If the MAC address is not found, pending the packet and send ARP request
	log(WARNING, "No ARP cache entry for " IP_FMT ", pending packet.",
	    HOST_IP_FMT_STR(dst_ip));

	// Pending the packet in the ARP cache
	arpcache_append_packet(iface, dst_ip, packet, len);

	// Send an ARP request
	log(DEBUG, "Sending ARP request for " IP_FMT,
	    HOST_IP_FMT_STR(dst_ip));
	arp_send_request(iface,dst_ip);
}
